/*
 * Toedo
 * Copyright (C) 2009-2010 J. Devauchelle and contributors.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * version 3 as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 */

package org.toedo.gui.views;

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.dialogs.IMessageProvider;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.FocusEvent;
import org.eclipse.swt.events.FocusListener;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Text;
import org.eclipse.swt.widgets.ToolBar;
import org.eclipse.swt.widgets.ToolItem;
import org.eclipse.ui.IActionBars;
import org.eclipse.ui.actions.ActionFactory;
import org.eclipse.ui.forms.events.HyperlinkAdapter;
import org.eclipse.ui.forms.events.HyperlinkEvent;
import org.eclipse.ui.forms.widgets.Form;
import org.eclipse.ui.forms.widgets.FormToolkit;
import org.eclipse.ui.forms.widgets.Section;
import org.eclipse.ui.part.ViewPart;
import org.toedo.controllers.DataController;
import org.toedo.controllers.ParserController;
import org.toedo.controllers.events.EventConstants;
import org.toedo.controllers.events.EventController;
import org.toedo.gui.translations.Messages;
import org.toedo.model.enums.WikiMarkupType;
import org.toedo.Activator;

public class ProjectPropertiesView extends ViewPart implements PropertyChangeListener {
	
	public static final String ID = "toedo-core.ProjectPropertiesView"; //$NON-NLS-1$

	private Composite mParent;
	private FormToolkit mToolkit;
	private Form mForm;
	
	private Text mProjectNameText;
	private Combo mWikiMarkupCombo;
	private Text mDisplayCssText;
	
	private Text mStartDateDelayText;	
	private Text mDefaultDurationText;	
	private Text mUrgentDelayText;	
	private Text mDefaultPriorityText;
	
	private Image mPropertiesImage;
	
	private Image mImportDefaultCssImage;
	private Image mClearCssImage;
	
	private IAction mDefaultCutAction;
	private IAction mDefaultCopyAction;
	private IAction mDefaultPasteAction;
	
	private boolean mEnableModification = false;
	
	private boolean mIsProjectNameModified = false;
	private boolean mIsWikiMarkupModified = false;
	private boolean mIsDisplayCssModified = false;
	
	private boolean mIsStartDateDelayModified = false;
	private boolean mIsDefaultDurationModified = false;
	private boolean mIsUrgentDelayModified = false;
	private boolean mIsDefaultPriorityModified = false;
	
	public ProjectPropertiesView() {
		Activator.getDefault();
		mPropertiesImage = Activator.getImageDescriptor("/icons/projectproperties-16.png").createImage(); //$NON-NLS-1$
		
		mImportDefaultCssImage = Activator.getImageDescriptor("/icons/import-css-16.png").createImage(); //$NON-NLS-1$
		mClearCssImage = Activator.getImageDescriptor("/icons/delete-16.png").createImage(); //$NON-NLS-1$
	}

	@Override
	public void createPartControl(Composite parent) {
		mParent = parent;
		
		mToolkit = new FormToolkit(mParent.getDisplay());
		mForm = mToolkit.createForm(mParent);
		mForm.setText(getPartName());
		mForm.setImage(mPropertiesImage);
		
		mToolkit.decorateFormHeading(mForm);
		
		mForm.addMessageHyperlinkListener(new HyperlinkAdapter() {
			@Override
			public void linkActivated(HyperlinkEvent e) {
				applyModifications();
			}
		});
		
		mForm.getBody().setLayout(new GridLayout(2, true));
		
		// General properties
		Section propertiesSection = mToolkit.createSection(mForm.getBody(), 
				Section.TITLE_BAR | Section.EXPANDED);
		propertiesSection.setLayoutData(new GridData(SWT.FILL, SWT.NONE, true, false));
		propertiesSection.setText(Messages.ProjectPropertiesView_GeneralPropertiesSection);
		
		Composite propertiesComposite = mToolkit.createComposite(propertiesSection);
		propertiesComposite.setLayout(new GridLayout(3, false));
		
		mToolkit.createLabel(propertiesComposite, Messages.ProjectPropertiesView_ProjectNameLabel);
		
		mProjectNameText = mToolkit.createText(propertiesComposite, ""); //$NON-NLS-1$
		mProjectNameText.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 2, 1));
		
		mProjectNameText.addModifyListener(new ModifyListener() {
			@Override
			public void modifyText(ModifyEvent e) {
				if (mEnableModification) {
					mIsProjectNameModified = true;
					setModified();				
				}
			}			
		});
		
		mProjectNameText.addFocusListener(new FocusListener() {			
			@Override
			public void focusLost(FocusEvent e) {
				unregisterStandardActions();
			}			
			@Override
			public void focusGained(FocusEvent e) {
				registerStandardActions(mProjectNameText);		
			}
		});
		
		mToolkit.createLabel(propertiesComposite, Messages.ProjectPropertiesView_WikiMarkup);
		
		mWikiMarkupCombo = new Combo(propertiesComposite, SWT.DROP_DOWN | SWT.READ_ONLY);
		mWikiMarkupCombo.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 2, 1));
		
		mWikiMarkupCombo.add("Textile"); //$NON-NLS-1$
		mWikiMarkupCombo.add("Mediawiki"); //$NON-NLS-1$
		mWikiMarkupCombo.add("Trac"); //$NON-NLS-1$
		mWikiMarkupCombo.add("Confluence"); //$NON-NLS-1$
		
		mWikiMarkupCombo.setData(Integer.toString(0), WikiMarkupType.EnTextile);
		mWikiMarkupCombo.setData(Integer.toString(1), WikiMarkupType.EnMediawiki);
		mWikiMarkupCombo.setData(Integer.toString(2), WikiMarkupType.EnTrac);
		mWikiMarkupCombo.setData(Integer.toString(3), WikiMarkupType.EnConfluence);
		
		mWikiMarkupCombo.addModifyListener(new ModifyListener() {
			@Override
			public void modifyText(ModifyEvent e) {
				if (mEnableModification) {
					mIsWikiMarkupModified = true;
					setModified();
				}				
			}			
		});
		
		mToolkit.paintBordersFor(propertiesComposite);
		propertiesSection.setClient(propertiesComposite);
		
		// Tasks properties
		Section tasksPropertiesSection = mToolkit.createSection(mForm.getBody(), 
				Section.TITLE_BAR | Section.EXPANDED);
		tasksPropertiesSection.setLayoutData(new GridData(SWT.FILL, SWT.NONE, true, false));
		tasksPropertiesSection.setText(Messages.ProjectPropertiesView_TasksPropertiesSection);
		
		Composite tasksPropertiesComposite = mToolkit.createComposite(tasksPropertiesSection);
		tasksPropertiesComposite.setLayout(new GridLayout(2, false));
		
		mToolkit.createLabel(tasksPropertiesComposite, Messages.ProjectPropertiesView_StartDateDelayLabel);
		
		mStartDateDelayText = mToolkit.createText(tasksPropertiesComposite, ""); //$NON-NLS-1$
		mStartDateDelayText.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
		
		mStartDateDelayText.addListener(SWT.Verify, new Listener() {
			@Override
			public void handleEvent(Event event) {
				checkIntegerInput(event);
			}			
		});
		
		mStartDateDelayText.addModifyListener(new ModifyListener() {			
			@Override
			public void modifyText(ModifyEvent e) {
				if (mEnableModification) {
					mIsStartDateDelayModified = true;
					setModified();
				}
			}
		});
		
		mStartDateDelayText.addFocusListener(new FocusListener() {			
			@Override
			public void focusLost(FocusEvent e) {
				unregisterStandardActions();
			}			
			@Override
			public void focusGained(FocusEvent e) {
				registerStandardActions(mStartDateDelayText);		
			}
		});
		
		mToolkit.createLabel(tasksPropertiesComposite, Messages.ProjectPropertiesView_DefaultDurationLabel);
		
		mDefaultDurationText = mToolkit.createText(tasksPropertiesComposite, ""); //$NON-NLS-1$
		mDefaultDurationText.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
		
		mDefaultDurationText.addListener(SWT.Verify, new Listener() {
			@Override
			public void handleEvent(Event event) {
				checkIntegerInput(event);
			}			
		});
		
		mDefaultDurationText.addModifyListener(new ModifyListener() {			
			@Override
			public void modifyText(ModifyEvent e) {
				if (mEnableModification) {
					mIsDefaultDurationModified = true;
					setModified();
				}
			}
		});
		
		mDefaultDurationText.addFocusListener(new FocusListener() {			
			@Override
			public void focusLost(FocusEvent e) {
				unregisterStandardActions();
			}			
			@Override
			public void focusGained(FocusEvent e) {
				registerStandardActions(mDefaultDurationText);		
			}
		});
		
		mToolkit.createLabel(tasksPropertiesComposite, Messages.ProjectPropertiesView_UrgentDelayLabel);
		
		mUrgentDelayText = mToolkit.createText(tasksPropertiesComposite, ""); //$NON-NLS-1$
		mUrgentDelayText.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
		
		mUrgentDelayText.addListener(SWT.Verify, new Listener() {
			@Override
			public void handleEvent(Event event) {
				checkIntegerInput(event);
			}			
		});
		
		mUrgentDelayText.addModifyListener(new ModifyListener() {			
			@Override
			public void modifyText(ModifyEvent e) {
				if (mEnableModification) {
					mIsUrgentDelayModified = true;
					setModified();
				}
			}
		});
		
		mUrgentDelayText.addFocusListener(new FocusListener() {			
			@Override
			public void focusLost(FocusEvent e) {
				unregisterStandardActions();
			}			
			@Override
			public void focusGained(FocusEvent e) {
				registerStandardActions(mUrgentDelayText);		
			}
		});
		
		mToolkit.createLabel(tasksPropertiesComposite, Messages.ProjectPropertiesView_DefaultPriorityLabel);
		
		mDefaultPriorityText = mToolkit.createText(tasksPropertiesComposite, ""); //$NON-NLS-1$
		mDefaultPriorityText.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
		
		mDefaultPriorityText.addListener(SWT.Verify, new Listener() {
			@Override
			public void handleEvent(Event event) {
				checkIntegerInput(event);
			}			
		});
		
		mDefaultPriorityText.addModifyListener(new ModifyListener() {			
			@Override
			public void modifyText(ModifyEvent e) {
				if (mEnableModification) {
					mIsDefaultPriorityModified = true;
					setModified();
				}
			}
		});
		
		mDefaultPriorityText.addFocusListener(new FocusListener() {			
			@Override
			public void focusLost(FocusEvent e) {
				unregisterStandardActions();
			}			
			@Override
			public void focusGained(FocusEvent e) {
				registerStandardActions(mDefaultPriorityText);		
			}
		});
		
		mToolkit.paintBordersFor(tasksPropertiesComposite);
		tasksPropertiesSection.setClient(tasksPropertiesComposite);
		
		// Display properties
		Section displaySection = mToolkit.createSection(mForm.getBody(), 
				Section.TITLE_BAR | Section.EXPANDED);
		displaySection.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 2, 1));
		displaySection.setText(Messages.ProjectPropertiesView_DisplayPropertiesSectionTitle);
		
		Composite displayComposite = mToolkit.createComposite(displaySection);
		displayComposite.setLayout(new GridLayout(1, false));
		
		ToolBar displaySectionToolBar = new ToolBar(displaySection, SWT.FLAT | SWT.HORIZONTAL);
		
		ToolItem importDefaultCssToolItem = new ToolItem(displaySectionToolBar, SWT.NONE);
		importDefaultCssToolItem.setImage(mImportDefaultCssImage);
		importDefaultCssToolItem.setToolTipText(Messages.ProjectPropertiesView_ImportDefaultCss);
		
		importDefaultCssToolItem.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent e) {
				mDisplayCssText.setText(ParserController.getInstance().getDefaultDisplayCss());
			}
		});
		
		new ToolItem(displaySectionToolBar, SWT.SEPARATOR);
		
		ToolItem clearCssToolItem = new ToolItem(displaySectionToolBar, SWT.NONE);
		clearCssToolItem.setImage(mClearCssImage);
		clearCssToolItem.setToolTipText(Messages.ProjectPropertiesView_ClearCss);
		
		clearCssToolItem.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent e) {
				mDisplayCssText.setText(""); //$NON-NLS-1$
			}
		});
		
		displaySection.setTextClient(displaySectionToolBar);
		
		Label cssLabel = mToolkit.createLabel(displayComposite, Messages.ProjectPropertiesView_DisplayCssLabel);
		cssLabel.setLayoutData(new GridData(SWT.FILL, SWT.NONE, true, false));
		
		mDisplayCssText = mToolkit.createText(displayComposite, null, SWT.MULTI | SWT.WRAP | SWT.V_SCROLL);
		mDisplayCssText.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
		
		mDisplayCssText.addModifyListener(new ModifyListener() {			
			@Override
			public void modifyText(ModifyEvent e) {
				if (mEnableModification) {
					mIsDisplayCssModified = true;
					setModified();
				}
			}
		});
		
		mDisplayCssText.addFocusListener(new FocusListener() {			
			@Override
			public void focusLost(FocusEvent e) {
				unregisterStandardActions();
			}			
			@Override
			public void focusGained(FocusEvent e) {
				registerStandardActions(mDisplayCssText);		
			}
		});
		
		mToolkit.paintBordersFor(displayComposite);
		displaySection.setClient(displayComposite);

		fillProperties();
		
		EventController.getInstance().addGeneralEventsListener(this);
	}
	
	private void registerStandardActions(final Text text) {
		mDefaultCutAction = new Action() {
			public void run() {
				text.cut();
			}
		};
		
		mDefaultCopyAction = new Action() {
			public void run() {
				text.copy();
			}
		};
		
		mDefaultPasteAction = new Action() {
			public void run() {
				text.paste();
			}
		};
		
		IActionBars actionBars = getViewSite().getActionBars();
		actionBars.clearGlobalActionHandlers();
		actionBars.setGlobalActionHandler(ActionFactory.CUT.getId(), mDefaultCutAction);
		actionBars.setGlobalActionHandler(ActionFactory.COPY.getId(), mDefaultCopyAction);
		actionBars.setGlobalActionHandler(ActionFactory.PASTE.getId(), mDefaultPasteAction);
		actionBars.updateActionBars();
	}
	
	private void unregisterStandardActions() {
		IActionBars actionBars = getViewSite().getActionBars();
		actionBars.clearGlobalActionHandlers();
		actionBars.updateActionBars();
	}

	private void fillProperties() {
		mEnableModification = false;
		
		String projectName = DataController.getInstance().getProject().getProjectName();
		setPartName(String.format(Messages.ProjectPropertiesView_Title, projectName));
		mForm.setText(getPartName());
		
		mProjectNameText.setText(projectName);		
		mStartDateDelayText.setText(Integer.toString(DataController.getInstance().getProject().getStartDateDelay()));
		mDefaultDurationText.setText(Integer.toString(DataController.getInstance().getProject().getDefaultDuration()));
		mUrgentDelayText.setText(Integer.toString(DataController.getInstance().getProject().getUrgentDelay()));
		mDefaultPriorityText.setText(Integer.toString(DataController.getInstance().getProject().getDefaultPriority()));
		
		switch (DataController.getInstance().getProject().getMarkupType()) {
		case EnTextile : mWikiMarkupCombo.setText(mWikiMarkupCombo.getItem(0)); break;
		case EnMediawiki : mWikiMarkupCombo.setText(mWikiMarkupCombo.getItem(1)); break;
		case EnTrac : mWikiMarkupCombo.setText(mWikiMarkupCombo.getItem(2)); break;
		case EnConfluence : mWikiMarkupCombo.setText(mWikiMarkupCombo.getItem(3)); break;
		default : mWikiMarkupCombo.setText(mWikiMarkupCombo.getItem(0));
		}
		
		mDisplayCssText.setText(DataController.getInstance().getProject().getDisplayCss());
		
		mEnableModification = true;
	}
	
	private void checkIntegerInput(Event event) {
		String text = event.text;
        for (int i = 0; i < text.length (); i++) {
                char ch = text.charAt (i);
                if (!('0' <= ch && ch <= '9')) {
                        event.doit = false;
                        return;
                }
        }
	}
	
	private void setModified() {
		if ((mIsProjectNameModified) ||
				(mIsWikiMarkupModified) ||
				(mIsStartDateDelayModified) ||
				(mIsDefaultDurationModified) ||
				(mIsUrgentDelayModified) ||
				(mIsDefaultPriorityModified) ||
				(mIsDisplayCssModified)) {
			mForm.setMessage(Messages.ProjectPropertiesView_ProjectChangeMessage, IMessageProvider.INFORMATION);
		} else {
			mForm.setMessage(null);
		}
	}
	
	private void applyModifications() {
		if (mIsProjectNameModified) {
			changeProjectName();
			mIsProjectNameModified = false;
		}
		if (mIsWikiMarkupModified) {
			changeWikiMarkupType();
			mIsWikiMarkupModified = false;
		}
		if (mIsStartDateDelayModified) {
			changeStartDateDelay();
			mIsStartDateDelayModified = false;
		}
		if (mIsDefaultDurationModified) {
			changeDefaultDuration();
			mIsDefaultDurationModified = false;
		}
		if (mIsUrgentDelayModified) {
			changeUrgentDelay();
			mIsUrgentDelayModified = false;
		}
		if (mIsDefaultPriorityModified) {
			changeDefaultPriority();
			mIsDefaultPriorityModified = false;
		}
		if (mIsDisplayCssModified) {
			changeDisplayCss();
			mIsDisplayCssModified = false;
		}
		setModified();
		fillProperties();
	}
	
	private void changeProjectName() {
		String newName = mProjectNameText.getText();
		
		if ((newName != null) &&
				(!newName.isEmpty())) {
			DataController.getInstance().getProject().setProjectName(newName);
		}
	}
	
	private void changeStartDateDelay() {
		String startDateDelayString = mStartDateDelayText.getText();
		
		if ((startDateDelayString != null) &&
				(!startDateDelayString.isEmpty())) {
			try {

				int startDateDelay = Integer.parseInt(startDateDelayString);

				DataController.getInstance().getProject().setStartDateDelay(startDateDelay);

			} catch (Exception e) {
				// Do nothing, will fallback to previous value.
			}
		}
	}
	
	private void changeDefaultDuration() {
		String defaultdurationString = mDefaultDurationText.getText();
		
		if ((defaultdurationString != null) &&
				(!defaultdurationString.isEmpty())) {
			try {

				int defaultDuration = Integer.parseInt(defaultdurationString);

				DataController.getInstance().getProject().setDefaultDuration(defaultDuration);

			} catch (Exception e) {
				// Do nothing, will fallback to previous value.
			}
		}
	}
	
	private void changeUrgentDelay() {
		String urgentDelayString = mUrgentDelayText.getText();
		
		if ((urgentDelayString != null) &&
				(!urgentDelayString.isEmpty())) {
			try {

				int urgentDelay = Integer.parseInt(urgentDelayString);

				DataController.getInstance().getProject().setUrgentDelay(urgentDelay);

			} catch (Exception e) {
				// Do nothing, will fallback to previous value.
			}
		}
	}
	
	private void changeDefaultPriority() {
		String defaultPriorityString = mDefaultPriorityText.getText();
		
		if ((defaultPriorityString != null) &&
				(!defaultPriorityString.isEmpty())) {
			try {

				int defaultpriority = Integer.parseInt(defaultPriorityString);

				DataController.getInstance().getProject().setDefaultPriority(defaultpriority);

			} catch (Exception e) {
				// Do nothing, will fallback to previous value.
			}
		}
	}
	
	private void changeWikiMarkupType() {
		WikiMarkupType newType = (WikiMarkupType) mWikiMarkupCombo.getData(Integer.toString(mWikiMarkupCombo.getSelectionIndex()));
		
		DataController.getInstance().getProject().setMarkupType(newType);
	}
	
	private void changeDisplayCss() {
		DataController.getInstance().getProject().setDisplayCss(mDisplayCssText.getText());
	}
	
	@Override
	public void setFocus() {
		// TODO Auto-generated method stub
	}

	@Override
	public void propertyChange(PropertyChangeEvent arg0) {
		
		class FullRefresher implements Runnable {					
			public void run() {											
				fillProperties();
			}
		}
		
		if ((arg0.getPropertyName().equals(EventConstants.EVT_OPEN)) ||
				(arg0.getPropertyName().equals(EventConstants.EVT_PROJECT_PROPERTIES_CHANGE))) {
			Display.getDefault().asyncExec(new FullRefresher());
		}
	}
	
	public void dispose() {
		mPropertiesImage.dispose();
		
		mImportDefaultCssImage.dispose();
		mClearCssImage.dispose();
		
		EventController.getInstance().removeGeneralEventsListener(this);
	}

}
